#include "stdafx.h"
#include "pyhgeCore.h"
#include "pyhgeParticleSystem.h"
PyTypeObject ParticleSystemType = {
	PyObject_HEAD_INIT(NULL)
		0,                         /*ob_size*/
		"hge.ParticleSystem",             /*tp_name*/
		sizeof(pyParticleSystem), /*tp_basicsize*/
		0,                         /*tp_itemsize*/
		0,                         /*tp_dealloc*/
		0,                         /*tp_print*/
		0,                         /*tp_getattr*/
		0,                         /*tp_setattr*/
		0,                         /*tp_compare*/
		0,                         /*tp_repr*/
		0,                         /*tp_as_number*/
		0,                         /*tp_as_sequence*/
		0,                         /*tp_as_mapping*/
		0,                         /*tp_hash */
		0,                         /*tp_call*/
		0,                         /*tp_str*/
		0,                         /*tp_getattro*/
		0,                         /*tp_setattro*/
		0,                         /*tp_as_buffer*/
		Py_TPFLAGS_DEFAULT,        /*tp_flags*/
		"ParticleSystem(filename,sprite) -> ParticleSystem\n"
		"ParticleSystem(ParticleSystemInfo) -> ParticleSystem\n"
		"hgeParticleSystem is a HGE helper class for advanced 2D particle systems. ",/* tp_doc */
};
static hgeParticleSystem*
ParticleSystem_GetPtr(PyObject * self)
{
	if (PyObject_TypeCheck(self,&ParticleSystemType))
		return ((pyParticleSystem*)self)->ps;
	PyErr_SetString(PyExc_TypeError,"ParticleSystem object is required.");
	return 0;
}
/*
Update
MoveTo
Transpose
*/
DEFINE_PYTHON_FUNCTION(
	"P.Update(fDeltaTime) -> None\n"
	"Updates the particle system.",
	METH_VARARGS,ParticleSystem_Update)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return NULL;
	float delta;
	if (!PyArg_ParseTuple(args,"f:Update",&delta))
		return NULL;
	ps->Update(delta);
	Py_RETURN_NONE;
}

DEFINE_PYTHON_FUNCTION(
	"P.MoveTo(x,y,bMoveParticles = False) -> None\n"
	"Moves particle system to a new position. ",
	METH_VARARGS,ParticleSystem_MoveTo)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float x,y;
	bool bMoveParticles = false;
	PyObject * bMove = NULL;
	if (!PyArg_ParseTuple(args,"ff|O:MoveTo",&x,&y,&bMove))
		return NULL;
	if (bMove)
	{
		int bval = PyObject_IsTrue(bMove);
		if (bval == -1) return NULL;
		bMoveParticles = bval != 0;
	}
	ps->MoveTo(x,y,bMoveParticles);
	Py_RETURN_NONE;
}

DEFINE_PYTHON_FUNCTION(
	"P.Transpose(x,y) -> None\n"
	"Adds constant particle system's coordinates shift.",
	METH_VARARGS,
	ParticleSystem_Transpose)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float x,y;
	if (!PyArg_ParseTuple(args,"ff:Transpose",&x,&y))
		return NULL;
	ps->Transpose(x,y);
	Py_RETURN_NONE;
}
/*
SetScale
FireAt
Fire
*/
DEFINE_PYTHON_FUNCTION(
	"P.SetScale(scale) -> None\n"
	"Scales the particle system.",
	METH_VARARGS,ParticleSystem_SetScale)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float scale;
	if (!PyArg_ParseTuple(args,"f:SetScale",&scale))
		return NULL;
	ps->SetScale(scale);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
	"P.FireAt(x,y) -> None\n"
	"Fires the particle system at the specified screen position. ",
	METH_VARARGS,ParticleSystem_FireAt)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float x,y;
	if (!PyArg_ParseTuple(args,"ff:FireAt",&x,&y))
		return NULL;
	ps->FireAt(x,y);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
	"P.Fire() -> None\n"
	"Fires the particle system.",
	METH_NOARGS,ParticleSystem_Fire)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	ps->Fire();
	Py_RETURN_NONE;
}
/*
Stop
Render
TrackBoundingBox
*/
DEFINE_PYTHON_FUNCTION(
	"P.Stop(bKillParticles=False) -> None\n"
	"Stops the particle system.",
	METH_VARARGS,ParticleSystem_Stop)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	bool bKillParticles = false;
	int bval;
	PyObject * pbv = NULL;
	if (!PyArg_ParseTuple(args,"|O:Stop",&pbv))
		return NULL;
	if (pbv)
	{
		bval = PyObject_IsTrue(pbv);
		if (bval == -1) return NULL;
		bKillParticles = bval > 0;
	}
	ps->Stop(bKillParticles);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
	"P.Render() -> None\n"
	"Renders the particle system to the screen.",
	METH_NOARGS,ParticleSystem_Render)
{
	//DebugBreak();
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	ps->Render();
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
	"P.TrackBoundingBox(bTrack) -> None\n"
	"Turns particle system's bounding box tracking on/off.",
	METH_VARARGS,ParticleSystem_TrackBoundingBox)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	bool bTrack;
	int bval;
	PyObject * pbv;
	if (!PyArg_ParseTuple(args,"O:TrackBoundingBox",&pbv))
		return NULL;
	bval = PyObject_IsTrue(pbv);
	if (bval == -1) return NULL;
	bTrack = bval > 0;
	ps->TrackBoundingBox(bTrack);
	Py_RETURN_FALSE;
}
/*
GetBoundingBox
GetParticlesAlive
GetPosition
GetTransposition
GetScale
GetAge
*/

DEFINE_PYTHON_FUNCTION(
	"P.GetBoundingBox() -> (x1,y1,x2,y2)\n"
	"Returns the particle system bounding box.",
	METH_NOARGS,ParticleSystem_GetBoundingBox)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	hgeRect rect;
	ps->GetBoundingBox(&rect);
	return Py_BuildValue("ffff",rect.x1,rect.y1,rect.x2,rect.y2);
}
DEFINE_PYTHON_FUNCTION("P.GetParticlesAlive() -> int\n"
					   "Returns the number of particles currently alive. ",
					   METH_NOARGS,ParticleSystem_GetParticlesAlive)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	return PyInt_FromLong(ps->GetParticlesAlive());
}
DEFINE_PYTHON_FUNCTION("P.GetPosition() -> (x,y)\n"
					   "Returns the particle system position.",
					   METH_NOARGS,ParticleSystem_GetPosition)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float x,y;
	ps->GetPosition(&x,&y);
	return Py_BuildValue("ff",x,y);
}
DEFINE_PYTHON_FUNCTION("P.GetTransposition() -> (dx,dy)\n"
					   "Returns the particle system's coordinatres shift. ",
					   METH_NOARGS,ParticleSystem_GetTransposition)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	float dx,dy;
	ps->GetTransposition(&dx,&dy);
	return Py_BuildValue("ff",dx,dy);
}
DEFINE_PYTHON_FUNCTION("P.GetScale() -> float\n"
					   "Returns the current scale of the particle system.",METH_NOARGS,ParticleSystem_GetScale)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	return PyFloat_FromDouble((double)ps->GetScale());
}
DEFINE_PYTHON_FUNCTION("P.GetAge() -> float\n"
					   "Returns the particle system's age. ",METH_NOARGS,ParticleSystem_GetAge)
{
	hgeParticleSystem * ps = ParticleSystem_GetPtr(self);
	if (!ps) return 0;
	return PyFloat_FromDouble((double)ps->GetAge());
}
static PyMethodDef ParticleSystem_methods[] = {
	{"Update",ParticleSystem_Update,ParticleSystem_Update_flag,ParticleSystem_Update_doc},
	{"MoveTo",ParticleSystem_MoveTo,ParticleSystem_MoveTo_flag,ParticleSystem_MoveTo_doc},
	{"Transpose",ParticleSystem_Transpose,ParticleSystem_Transpose_flag,ParticleSystem_Transpose_doc},
	{"SetScale",ParticleSystem_SetScale,ParticleSystem_SetScale_flag,ParticleSystem_SetScale_doc},
	{"FireAt",ParticleSystem_FireAt,ParticleSystem_FireAt_flag,ParticleSystem_FireAt_doc},
	{"Fire",ParticleSystem_Fire,ParticleSystem_Fire_flag,ParticleSystem_Fire_doc},
	{"Stop",ParticleSystem_Stop,ParticleSystem_Stop_flag,ParticleSystem_Stop_doc},
	{"Render",ParticleSystem_Render,ParticleSystem_Render_flag,ParticleSystem_Render_doc},
	{"TrackBoundingBox",ParticleSystem_TrackBoundingBox,ParticleSystem_TrackBoundingBox_flag,ParticleSystem_TrackBoundingBox_doc},
	
	{"GetBoundingBox",ParticleSystem_GetBoundingBox,ParticleSystem_GetBoundingBox_flag,ParticleSystem_GetBoundingBox_doc},
	{"GetParticlesAlive",ParticleSystem_GetParticlesAlive,ParticleSystem_GetParticlesAlive_flag,ParticleSystem_GetParticlesAlive_doc},
	{"GetPosition",ParticleSystem_GetPosition,ParticleSystem_GetPosition_flag,ParticleSystem_GetPosition_doc},
	{"GetTransposition",ParticleSystem_GetTransposition,ParticleSystem_GetTransposition_flag,ParticleSystem_GetTransposition_doc},
	{"GetScale",ParticleSystem_GetScale,ParticleSystem_GetScale_flag,ParticleSystem_GetScale_doc},
	{"GetAge",ParticleSystem_GetAge,ParticleSystem_GetAge_flag,ParticleSystem_GetAge_doc},
	{0}
};
/*
others function
*/
static PyObject *
ParticleSystem_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	pyParticleSystem * self = (pyParticleSystem*)type->tp_alloc(type, 0);
	if (!self) return NULL;
	self->ps = NULL;
	self->pinfo = NULL;
	return (PyObject*)self;
}
static void
ParticleSystem_dealloc(PyObject * _self)
{
	pyParticleSystem * self = (pyParticleSystem*)_self;
	Py_XDECREF(self->pinfo);
	delete self->ps;
	self->ob_type->tp_free((PyObject*)self);
}

static int
ParticleSystem_init(PyObject *_self, PyObject *args, PyObject *kwds)
{
	//param: filename,sprite
	//param: info,sprite
	pyParticleSystem * self = (pyParticleSystem*)_self;
	PyObject * arg1,*sprite;
	if (!PyArg_ParseTuple(args,"OO:ParticleSystem",&arg1,&sprite))
		return -1;
	if (!PyObject_TypeCheck(sprite,&SpriteType))
	{
		PyErr_SetString(PyExc_TypeError,"Second argument must be Sprite.");
		return -1;
	}
	hgeSprite * pSprite = ((pySprite*)(sprite))->phgeSprite;
	if (!pSprite)
	{
		PyErr_SetString(PyExc_ValueError,"Invalid sprite.");
		return -1;
	}
	if (PyString_Check(arg1))
	{
		const char * filename = PyString_AS_STRING(arg1);
		//check filename first
		void * psi = hge->Resource_Load(filename);
		if (!psi)
		{
			PyErr_Format(pyHGEError,"Error load resource %s",filename);
			return NULL;
		}
		hge->Resource_Free(psi);
		//end check
		self->ps = new hgeParticleSystem(filename,pSprite);
		self->pinfo = pyNewParticleSystemInfo(&(self->ps->info),(pySprite*)sprite);
		if (0!=pyParticleSystemInfo_BindInfo((PyObject*)(self->pinfo),&(self->ps->info),(PyObject*)self))
			return -1;
		if (!self->pinfo) return -1;
	}
	else if (PyObject_TypeCheck(arg1,&ParticleSystemInfoType))
	{
		self->ps = new hgeParticleSystem( (((pyParticleSystemInfo*)arg1)->psi) );
		Py_INCREF(arg1);
		self->pinfo = (pyParticleSystemInfo*)arg1;
		if (0!=pyParticleSystemInfo_BindInfo((PyObject*)(self->pinfo),&(self->ps->info),(PyObject*)self))
			return -1;
		return 0;
	}
	else
	{
		PyErr_SetString(PyExc_TypeError,"First argument must be string or ParticleSystemInfo.");
		return -1;
	}
	return 0;
}
static PyObject *
ParticleSystem_repr(PyObject * _self)
{
	pyParticleSystem * self = (pyParticleSystem*)_self;
	return PyString_FromFormat("<ParticleSystem at %p>",self->ps);
}
static PyMemberDef ParticleSystem_members[] = {
	{"info",T_OBJECT,offsetof(pyParticleSystem,pinfo),READONLY,""},
	{NULL}  /* Sentinel */
};
int
pyInitParticleSystem(PyObject * mod)
{
	ParticleSystemType.tp_new = ParticleSystem_new;
	ParticleSystemType.tp_dealloc = ParticleSystem_dealloc;
	ParticleSystemType.tp_init = ParticleSystem_init;
	ParticleSystemType.tp_repr = ParticleSystem_repr;
	ParticleSystemType.tp_str = ParticleSystem_repr;
	ParticleSystemType.tp_methods = ParticleSystem_methods;
	ParticleSystemType.tp_members = ParticleSystem_members;
	if (PyType_Ready(&ParticleSystemType) <0) return -1;
	Py_INCREF(&ParticleSystemType);
	return PyModule_AddObject(mod, "ParticleSystem", (PyObject *)&ParticleSystemType); 
}